﻿using System.Linq;
using System.Text;
using CodeGenerationPipeline;
using CodeGenerationPipeline.Contracts;
using System.IO;
using AbstraX;
using AbstraX.Contracts;
using System.Diagnostics;
using Utils;
using AbstraX.TypeMappings;
using AbstraX.ServerInterfaces;
using GeneratorLibrary;
using System.Text.RegularExpressions;
using AbstraX.Bindings;
using AbstraX.AssemblyInterfaces;
using ClassWrapper;
using System.Collections.Generic;
using VisualStudioProvider.Configuration;
using System;
using VisualStudioProvider;
using Microsoft.Build.Framework;
using Utils;

namespace CsParser
{
    public class CsParserGenerator : ClassWrapperGenerator
    {
        public override event WatchProcess OnWatchProcess;
        public override event OnFileGeneratedHandler OnFileGenerated;
        public static ReplaceStandardFileNameHandler ReplaceStandardFileName { get; set; }

        public override string Name
        {
            get
            {
                return "CsParser Generator";
            }
        }

        public override string Description
        {
            get
            {
                return "";
            }
        }

        public override ICodeGenerationPackage GenerateFrom(Dictionary<string, IElementBuild> builds, DirectoryInfo workspaceDirectory)
        {
            try
            {
                if (!VSConfigProvider.TemplatesReady)
                {
                    EventsService.PostMessage(Message.GeneralMessage, "Waiting for project template indexing.");
                }

                var template = VSConfigProvider.ProjectTemplates[@"\CsParser\CsParser"];
                var interfacesAdded = new Dictionary<ElementBuild, BaseType>();

                // testing code

                var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
                var directory = Path.Combine(desktop, @"TestGenerator\CsParser");
                var directoryInfo = new DirectoryInfo(directory);

                if (directoryInfo.Exists)
                {
                    directoryInfo.ForceDelete();
                    directoryInfo.Create();
                }

                // end - testing code

                var parms = new VSTemplateParameters
                {
                    ProjectName = "CsParser",
                    FrameworkVersion = "4.0",
                    RegisteredOrganization = "Mind Chemistry Inc.",
                    CopyrightYear = "2012"
                };

                EventsService.PostMessage(Message.GeneralMessage, string.Format("Processing '{0}' project template.", template.Name));

                template.CopyAndProcess(directory, parms);

                var project = new VSProject(Path.Combine(directory, "CsParser.csproj"));
                var interfaceDefinitions = new Dictionary<string, IElementBuild>();

                EventsService.PostMessage(Message.GeneralMessage, "Generating code.");

                templateEngineHost.DebugCallback += (sender, e2) =>
                {
                    var buildElement = ((GeneratorBase)sender).Element;

                    if (e2 is EventArgs<NavigationItem>)
                    {
                        var navigationItem = ((EventArgs<NavigationItem>)e2).Value;
                    }
                    else if (e2 is EventArgs<IElement>)
                    {
                        var element = ((EventArgs<IElement>)e2).Value;
                    }
                };
                                
                TypeIsToBeBuiltHandler typeIsToBeBuiltHandler = (BaseType type, out bool isToBeBuilt) =>
                {
                    if (type.Namespace == "Metaspec")
                    {
                        isToBeBuilt = true;
                    }
                    else
                    {
                        isToBeBuilt = false;
                    }
                };

                templateEngineHost.TypeIsToBeBuilt += typeIsToBeBuiltHandler;

                ElementIsToBeBuiltHandler elementIsToBeBuiltHandler = (IElement element, out bool isToBeBuilt) =>
                {
                    if (element.DataType.Namespace == "Metaspec")
                    {
                        isToBeBuilt = true;
                    }
                    else
                    {
                        isToBeBuilt = false;
                    }
                };

                templateEngineHost.ElementIsToBeBuilt += elementIsToBeBuiltHandler;

                ReplaceStandardNameHandler replaceStandardName = (ref string name) =>
                {
                    if (name.IndexOf('_') != -1)
                    {
                        var parts = name.Split('_').Where(p => p.Length > 0);

                        name = string.Join("", parts.Select(p => p.ProperCase()));
                    }
                    else
                    {
                        name = name.ProperCase();
                    }
                };

                ReplaceStandardNameHandler replaceStandardTypeName = (ref string name) =>
                {
                    name = Regex.Replace(name, "^Cs", "Code");
                    name = Regex.Replace(name, "^cs", "code");

                    replaceStandardName(ref name);

                    if (name.IndexOf('<') > -1)
                    {
                        var regex = new Regex(@"(?<name>\w*?)\<(?<generics>\w*?)\>");

                        if (regex.IsMatch(name))
                        {
                            var match = regex.Match(name);
                            var typeName = match.Groups["name"].Value;
                            var generics = match.Groups["generics"].Captures;

                            typeName += "_";

                            foreach (Group capture in generics)
                            {
                                typeName += capture.Value + "-";
                            }

                            typeName = typeName.RemoveEnd(1);

                            typeName += "_";

                            name = typeName;
                        }
                    }
                };

                CsParserGenerator.ReplaceStandardFileName = (string name) =>
                {
                    replaceStandardTypeName(ref name);

                    return name;
                };

                templateEngineHost.GetBuildName += (IElementBuild build, ref string name) =>
                {
                    replaceStandardTypeName(ref name);
                };

                templateEngineHost.GetElementName += (IElement element, ref string name) =>
                {
                    replaceStandardTypeName(ref name);
                };

                templateEngineHost.GetTypeName += (BaseType type, ref string name) =>
                {
                    replaceStandardTypeName(ref name);
                };

                templateEngineHost.GetPropertyBindingPropertyName += (IPropertyBinding binding, ref string name) =>
                {
                    replaceStandardName(ref name);
                };

                templateEngineHost.GetNavigationPropertyName += (NavigationItem navigationItem, ref string name) =>
                {
                    replaceStandardName(ref name);
                };

                templateEngineHost.GetMethodOperationName += (IMethodOperation operation, ref string name) =>
                {
                    replaceStandardName(ref name);
                };

                templateEngineHost.GetAttributeName += (IAttribute attribute, ref string name) =>
                {
                    replaceStandardName(ref name);
                };

                templateEngineHost.GetReferencedBuildName += (IElementBuild build, ref string name) =>
                {
                    if (build.Element.DataType.Namespace == "Metaspec")
                    {
                        replaceStandardTypeName(ref name);

                        if (build.Element.Kind != DefinitionKind.Interface && build.Element.Kind != DefinitionKind.Enumeration)
                        {
                            name = "I" + name;
                        }
                    }
                };

                templateEngineHost.GetReferencedElementName += (IElement element, ref string name) =>
                {
                    if (element.DataType.Namespace == "Metaspec")
                    {
                        replaceStandardTypeName(ref name);

                        if (element.Kind != DefinitionKind.Interface && element.Kind != DefinitionKind.Enumeration)
                        {
                            name = "I" + name;
                        }
                    }
                };

                templateEngineHost.GetReferencedTypeName += (BaseType type, ref string name) =>
                {
                    if (type.Namespace == "Metaspec")
                    {
                        replaceStandardTypeName(ref name);

                        if (!type.IsInterface && !type.IsEnum)
                        {
                            name = "I" + name;
                        }
                    }
                };

                templateEngineHost.AbortBuild += (sender, e2) =>
                {
                    throw new StepAbortionException(e2.LastFile);
                };

                templateEngineHost.DefineConstruct += (IElementBuild build, ConstructType constructType, out BaseType baseType) =>
                {
                    baseType = null;

                    switch (constructType)
                    {
                        case ConstructType.Interface:

                            var name = "I" + build.Element.Name.Standardize();

                            if (!interfaceDefinitions.ContainsKey(name))
                            {
                                var codeFile = new FileInfo(Path.Combine(workspaceDirectory.FullName, name + ".cs"));
                                var subPath = @"Interfaces\";

                                templateEngineHost.GenerateFile<ElementInterfaceGenerator>(build, codeFile, "AbstraX.ParserInterfaces");

                                OnFileGenerated(project, subPath, codeFile);
                                project.AddCompileFile(codeFile.FullName, subPath + codeFile.Name);

                                interfaceDefinitions.Add(name, build);
                            }

                            baseType = new BaseType
                            {
                                Name = name
                            };

                            break;
                    }
                };

                var buildsOrdered = builds.Values.OrderBy(b => b.Element.Name);
                var metaspecBuilds = builds.Values.Where(b => 
                {
                    return b.Element.IsToBeBuilt(elementIsToBeBuiltHandler, typeIsToBeBuiltHandler);

                }).OrderBy(b => b.Element.Name);
 
                templateEngineHost.GeneratorStack.Push(this);
                templateEngineHost.EmitDebugInfoLevel = EmitDebugInfoLevel.Diagnostics;

                templateEngineHost.DefineType += (IElementBuild build, BaseType type, out BaseType baseType) =>
                {
                    baseType = null;

                    if (type.Namespace == "Metaspec")
                    {
                        var e = metaspecBuilds.Single(b => b.Element.DataType.FullyQualifiedName == type.FullyQualifiedName);
                        var name = e.Element.Name.Standardize();

                        if (!e.IsBuilt)
                        {
                            var codeFile = new FileInfo(Path.Combine(workspaceDirectory.FullName, name + ".cs"));
                            var subPath = @"Implementations\";

                            templateEngineHost.GenerateFile<ElementClassGenerator>(e, codeFile, "CsParser");

                            OnFileGenerated(project, subPath, codeFile);
                            project.AddCompileFile(codeFile.FullName, subPath + codeFile.Name);

                            e.IsBuilt = true;
                        }

                        baseType = new BaseType
                        {
                            Name = name
                        };
                    }
                };

                metaspecBuilds.ToList<IElementBuild>().ForEach(e =>
                {
                    if (!e.IsBuilt)
                    {
                        var name = e.Element.Name.Standardize();
                        var codeFile = new FileInfo(Path.Combine(workspaceDirectory.FullName, name + ".cs"));

                        if (e.Element.Kind == DefinitionKind.Enumeration)
                        {
                            var subPath = @"Enums\";

                            templateEngineHost.GenerateFile<EnumGenerator>(e, codeFile, "AbstraX.ParserInterfaces");

                            OnFileGenerated(project, subPath, codeFile);
                            project.AddCompileFile(codeFile.FullName, subPath + codeFile.Name);
                        }
                        else if (e.Element.Kind == DefinitionKind.Interface)
                        {
                            if (!interfaceDefinitions.ContainsKey(name))
                            {
                                var subPath = @"Interfaces\";

                                templateEngineHost.GenerateFile<ElementInterfaceGenerator>(e, codeFile, "AbstraX.ParserInterfaces");

                                OnFileGenerated(project, subPath, codeFile);
                                project.AddCompileFile(codeFile.FullName, subPath + codeFile.Name);

                                interfaceDefinitions.Add(name, e);
                            }
                        }
                        else
                        {
                            var subPath = @"Implementations\";

                            templateEngineHost.GenerateFile<ElementClassGenerator>(e, codeFile, "CsParser");

                            OnFileGenerated(project, subPath, codeFile);
                            project.AddCompileFile(codeFile.FullName, subPath + codeFile.Name);
                        }

                        e.IsBuilt = true;
                    }
                });

                this.EventSources.Add(project);

                EventsService.PostMessage(Message.RequestingBuild, this.Name);

                project.Verbosity = LoggerVerbosity.Minimal;
                project.Compile();

                EventsService.PostMessage(Message.BuildRequestCompleted, this.Name);

                templateEngineHost.GeneratorStack.Pop();
            }
            catch (StepAbortionException ex)
            {
                this.LastException = ex;
                return null;
            }
            catch (Exception ex)
            {
                this.LastException = ex;
                return null;
            }

            return null;
        }

        public override ICodeGenerationPackage GenerateFromPackage(ICodeGenerationPackage incomingPackage)
        {
            throw new NotImplementedException();
        }

        public override IProgrammingLanguage ProgrammingLanguage
        {
            get
            {
                return domainHost.AvailableLanguages.Single(l => l.WellKnownLanguage == WellKnownLanguage.CSharp);
            }
        }
    }

    public static class CsParserGeneratorExtensions
    {
        public static string Standardize(this string name)
        {
            return CsParserGenerator.ReplaceStandardFileName(name);
        }
    }
}
